/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * pawsCanvas.java
 *
 * Created on Mar 15, 2010, 1:48:33 AM
 */

package paws.gui;

import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.Color;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Set ;
import java.util.HashSet ;
import java.util.Random ;

/**
 * 
 * @author Greg
 */

public class PawsCanvas extends javax.swing.JPanel {

    /**
     * 
     */
    private static final long serialVersionUID = -1254049632110039045L;

    /** Creates new form pawsCanvas */

    
    private final int SIZE = 5; // radius of each dot
    

    private Set<paws.gui.Node> nodeSet;
    private Set<paws.gui.Node> selectedNodes;
    
    private paws.gui.CursorMode canvasState ;//State the canvas is in ie Select, Uniform, NonUniform, Single, etc.
    private Point dragStart, dragEnd ; //start and end points for click and drag rectangle
    private paws.gui.Node.Type nodeType ;//type of node to place
    private int density ; //density of nodes for uniform and NonUniform
    private int numClusters ; //number of clusters to be dropped
    private int nodesInCluster ;
    private int nodesPerCluster ;
    private int clusterRadius ;  //density/radius of the cluster

    public PawsCanvas() {
        initComponents();

        selectedNodes = new HashSet<paws.gui.Node>();
        nodeSet = new HashSet<paws.gui.Node>();
        nodeType = paws.gui.Node.Type.Intermediary ;
        
        canvasState = new paws.gui.CursorMode() ;
        dragStart = new Point() ;
        dragEnd = new Point() ;
        numClusters = 0 ;
        nodesInCluster = 0 ;
        nodesPerCluster = 0;

        addMouseListener(new DotsListener());
        addMouseMotionListener(new DotsListener()) ;

        setBackground(Color.LIGHT_GRAY);
        setPreferredSize(new Dimension(1000, 700));
    }

    //get the location of the cursor on the canvas
    public Point getCursorLocation() {

        return getMousePosition();
    }

    //get the number of Total Nodes
    public int getNodeCount() {
        return nodeSet.size();
    }
    
    public Set<Node> getNodes() {
      return nodeSet;
    }

    @Override
    public void paintComponent(Graphics page) {
        super.paintComponent(page);

        //Painting for intermediary nodes
        
        for (paws.gui.Node spot : nodeSet)
        {
            if(spot.getType().equals(paws.gui.Node.Type.Intermediary))
            {
                page.setColor(Color.black);
                page.fillOval(spot.getX() - SIZE, spot.getY() - SIZE, SIZE * 2, SIZE * 2);
            }

            if(spot.getType().equals(paws.gui.Node.Type.End))
            {
                page.setColor(Color.RED) ;
                page.fillOval(spot.getX() - SIZE, spot.getY() - SIZE, SIZE * 2, SIZE * 2);
            }

            if(spot.getType().equals(paws.gui.Node.Type.Start))
            {
                page.setColor(Color.GREEN) ;
                page.fillOval(spot.getX() - SIZE, spot.getY() - SIZE, SIZE * 2, SIZE * 2);
            }
            
        }

        //paint selected Nodes
        for (paws.gui.Node spot : selectedNodes)
        {
            page.setColor(Color.orange) ;
            page.fillOval(spot.getX() - SIZE, spot.getY() - SIZE, SIZE * 2, SIZE * 2);
        }

        //draw click and drag rectangle
        if(canvasState.isUniform() || canvasState.isNonUniform())
        {
            page.setColor(Color.magenta);
            page.fillRect(dragStart.x, dragStart.y, dragEnd.x - dragStart.x, dragEnd.y -dragStart.y) ;
           //debug statement page.drawString ("dragx " + getMousePosition().x + " dragy "+getMousePosition().y + "dragStart " + dragStart.toString(), 5, 15);
        }

         /*
        page.setColor(Color.white);
        page.fillOval(pointToDelete.getX() - SIZE, pointToDelete.getY() - SIZE, SIZE * 2,
                SIZE * 2);*/

    }

    //delete selected Nodes
    public void deleteNodes() {

        for(paws.gui.Node spot : selectedNodes)
        nodeSet.remove(spot);

        selectedNodes.clear() ;
        repaint();
    }

    public void deselectNodes()
    {
        selectedNodes.clear();
        repaint() ;
    }
    private void createUniformNodes(Point start, Point end)
    {
        int x1, x2 ;
        int y1, y2 ;
        int nodesDropped = 0 ;
        Point temp = new Point() ;
        Random random = new Random() ;
        int nodesToDrop = density - nodesInCluster + numClusters ;


        Set<Integer> clusterSet = new HashSet<Integer>() ;

        for(int i = 0 ; i < numClusters ; i ++)
        {
            clusterSet.add(random.nextInt(nodesToDrop));
        }

        System.out.println(clusterSet.toString()) ; ///TEST LINE

        if(start.x > end.x)
        {
            x1 = end.x ;
            x2 = start.x ;
        }
        else
        {
            x1 = start.x ;
            x2 = end.x ;
        }

        if(start.y > end.y)
        {
            y1 = end.y ;
            y2 = start.y ;
        }
        else
        {
            y1 = start.y ;
            y2 = end.y ;
        }


        Point sections = new Point(bestFactors(nodesToDrop)) ;

        for(int i = 0 ;  i < sections.x ; i++)
        {
            for(int p = 0 ; p < sections.y ; p++)
            {
              temp = new Point(Math.round(i*((x2-x1)/(sections.x-1))) + x1  , Math.round(p*(y2-y1)/(sections.y-1)) + y1) ;
               

               if (clusterSet.contains((nodesDropped)))
               {
                   System.out.println((i+p)) ;
                   createCluster(temp) ;
                   nodesDropped++ ;
               }
               else
            if (!locationUsed(temp)&& (nodesDropped < nodesToDrop))
            {
                    nodeSet.add(new paws.gui.Node(temp, nodeType));
                    nodesDropped++ ;
            }
            }

        }

        repaint() ;

    }

    private void createNonUniformNodes(Point start, Point end)
    {
        Point temp = new Point() ;
        Random random = new Random() ;

        int x1, x2 ;
        int y1, y2 ;

        Set<Integer> clusterSet = new HashSet<Integer>() ;

        System.out.println(numClusters) ;


        while(clusterSet.size() <  numClusters)
        {
           int i = 0 ;
           try
           {
               clusterSet.add(random.nextInt(density - nodesInCluster -i));
               i++ ;
           }
           catch(IllegalArgumentException iae)
           {
               int p = clusterSet.size() ;
               clusterSet.add(p) ;
           }
        }

        System.out.println(clusterSet.toString()) ;

        if(start.x > end.x)
        {
            x1 = end.x ;
            x2 = start.x ;
        }
        else
        {
            x1 = start.x ;
            x2 = end.x ;
        }

        if(start.y > end.y)
        {
            y1 = end.y ;
            y2 = start.y ;
        }
        else
        {
            y1 = start.y ;
            y2 = end.y ;
        }

        for(int i = 0 ;  i < (density - nodesInCluster + numClusters) ; i++)
        {
            int dx = (x2 - x1); 
            int dy = (y2 - y1);
            int rx = (dx > 0) ? random.nextInt(dx) + x1 : x1;
            int ry = (dy > 0) ? random.nextInt(dy) + y1 : y1;
            
            // temp = new Point(random.nextInt(x2-x1) + x1, random.nextInt(y2-y1) + y1) ;
            temp = new Point(rx, ry);
            
            if(clusterSet.contains(i))
            {
                createCluster(temp) ;
                System.out.println("It works!") ;
            }
            else
            if (!locationUsed(temp))
            {
                    nodeSet.add(new paws.gui.Node(temp, nodeType));
            }

        }

        if(density == nodesInCluster)
        {
            for(int i = 0; i < clusterSet.size(); i ++)
            {
                System.out.println(clusterSet.size()) ;
                temp = new Point(random.nextInt(x2-x1) + x1, random.nextInt(y2-y1) + y1) ;
                createCluster(temp) ;
            }
        }

    }

    private void createCluster(Point clusterPoint)
    {
        Random random = new Random() ;

        for(int i = 1 ; i <= nodesPerCluster ; i++)
        {
            //Point temp = clusterPoint ;
            //System.out.println(temp.toString()) ;
            //temp.translate(random.nextInt(clusterRadius) - random.nextInt(clusterRadius), random.nextInt(clusterRadius) -random.nextInt(clusterRadius)) ;
            nodeSet.add(new paws.gui.Node(new Point(clusterPoint.x + random.nextInt(clusterRadius) - random.nextInt(clusterRadius), clusterPoint.y +random.nextInt(clusterRadius) -random.nextInt(clusterRadius)), nodeType));
            System.out.println("cluster node added") ;
        }
    }

    public void clearAllNodes()
    {
        nodeSet.clear();

        repaint() ;
    }

    public String getClusterStats()
    {
        return ("Number of Clusters"+ numClusters +" Nodes in Cluster" + nodesInCluster+"\n Nodes total: "+ density) ;
    }
    //tells if a location on the canvas has been used already
    private boolean locationUsed(Point location)
    {
        for(paws.gui.Node spot : nodeSet )
        {
           if( spot.getLocation().distance(location.getLocation()) <= SIZE*2)
               return true ;
        }
        return false ;
    }

    //grabs the Node using the said location
    private paws.gui.Node getNodeInLocation(Point location)
    {
        for(paws.gui.Node spot : nodeSet )
        {
           if( spot.getLocation().distance(location.getLocation())  <= SIZE *2)
               return spot ;
        }
        return new Node() ;
    }


    private Point bestFactors(int nodes)
    {
        Point answer = new Point(10000000, 0) ;
        Set<Point> pointSet = new HashSet<Point>() ;

        for(int i =1 ; i <= nodes ; i ++)
        {
            if (nodes%i == 0)
            {
                pointSet.add(new Point(i, (nodes/i)));
            }
        }

        for (Point spot: pointSet)
        {
            if (Math.abs(answer.x - answer.y) > Math.abs(spot.x - spot.y))
                answer = spot ;
        }

        if (Math.abs(answer.x - answer.y) == (nodes - 1))
            return bestFactors((answer.x + answer.y)) ;
        
        return answer ;

    }
    // *****************************************************************
    // Represents the listener for mouse events.
    // *****************************************************************



    private class DotsListener implements MouseListener, MouseMotionListener {
        // --------------------------------------------------------------
        // Adds the current point to the list of points and redraws
        // the panel whenever the mouse button is pressed.
        // --------------------------------------------------------------
        public void mousePressed(MouseEvent event) {


            if(canvasState.isSingle())
            {
                if (!locationUsed(event.getPoint()))
                    nodeSet.add(new paws.gui.Node(event.getPoint(), nodeType));
            }
            if(canvasState.isSelect())
            {
                if(locationUsed(event.getPoint()))
                {
                    paws.gui.Node temp = getNodeInLocation(event.getPoint()) ;
                    if(selectedNodes.contains(temp))
                        selectedNodes.remove(temp) ;
                    else
                        selectedNodes.add(temp);
                }
            }

           
            if(canvasState.isUniform()|| canvasState.isNonUniform())
            {
                dragStart.move(event.getX(), event.getY()) ;
                dragEnd.setLocation(dragStart) ;
            }

            repaint();
        }

        public void mouseDragged(MouseEvent event)
        {
           if(canvasState.isUniform() || canvasState.isNonUniform())
            {
                dragEnd.setLocation(event.getPoint()) ;
                repaint() ;
            }
        }

        public void mouseMoved(MouseEvent event)
        {
            if(canvasState.isUniform() || canvasState.isNonUniform())
            {
                
                
                repaint() ;


            }
        }
        // --------------------------------------------------------------
        // Provide empty definitions for unused event methods.
        // --------------------------------------------------------------
        public void mouseClicked(MouseEvent event) {
            
        }

        public void mouseReleased(MouseEvent event) {


            if(canvasState.isUniform())
            {
            createUniformNodes(dragStart, dragEnd) ;
            }
            else if(canvasState.isNonUniform())
                createNonUniformNodes(dragStart, dragEnd) ;
            dragEnd = new Point() ;
            dragStart = new Point() ;
            repaint() ;
        }

        public void mouseEntered(MouseEvent event) {
        }

        public void mouseExited(MouseEvent event) {
        }
    }

    //Set the state of the canvas with this method
    public void setState(paws.gui.CursorMode state)
    {
        canvasState.setState(state.getState());
    }

    //set the density for NonUniformed or uniform
    public void setDensity(int newDensity)
    {
        density = newDensity ;
    }

    public void setClusterProperties(int count, int percentNodesInCluster, int clusterDensity)
    {
        numClusters = count ;
        nodesInCluster = (int)Math.ceil((double)density *((double)percentNodesInCluster/100)) ;
        nodesPerCluster = (int)Math.ceil((double)nodesInCluster/numClusters) ;
        clusterRadius = clusterDensity ;

        System.out.println("Number of clusters: "+ numClusters+ " Nodes in Cluster: " + nodesInCluster) ;
        System.out.println("Nodes per cluster: " + nodesPerCluster+"  Node Radius:" + clusterRadius) ;



    }


    //Set the state of the nodes to be dropped with this
    public void setNodeType(paws.gui.Node.Type newType)
    {
        nodeType = newType ;

        if (selectedNodes.size() > 0)
        {
            for(paws.gui.Node spot : selectedNodes)
            {

                nodeSet.remove(spot);
                spot.setType(nodeType) ;
                nodeSet.add(spot);
            }
        }
        repaint() ;
    }
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed"
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setName("Form"); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

}
